﻿using System.Diagnostics;
using System.Threading.Tasks;
using PetaPoco;
using PmSoft.Caching;
using PmSoft.DBContext;
using PetaPoco.Core;
using PmSoft.ApplicationContext;
using Microsoft.Extensions.DependencyInjection;
using PmSoft.Events;
using PmSoft.Logging;
using Modules;

namespace PmSoft.Repositories
{
    public class Repository<TEntity, TDbContext>
        where TEntity : class, IEntity
        where TDbContext : PetaPocoDbContext
    {

        public Repository(TDbContext dbContext, IApplicationContext applicationContext)
        {
            this.DbContext = dbContext;
            this.ApplicationContext = applicationContext;
            this.CacheablePageCount = 30;
            this.PrimaryMaxRecords = 50000;
            this.SecondaryMaxRecords = 5000;
        }

        protected IApplicationContext ApplicationContext { get; private set; }

        protected TDbContext DbContext { get; private set; }

        protected ICacheService CacheService { get { return ApplicationContext.GetService<ICacheService>(); } }

        /// <summary>
        /// 可缓存的列表缓存页数
        /// </summary>
        protected virtual int CacheablePageCount { get; private set; }
        /// <summary>
        /// 主流查询最大允许返回记录数 
        /// </summary>
        protected virtual long PrimaryMaxRecords { get; private set; }
        /// <summary>
        /// 非主流查询最大允许返回记录数
        /// </summary>
        protected virtual int SecondaryMaxRecords { get; private set; }

        /// <summary>
        /// 实体缓存服务类
        /// </summary>
        protected EntityCacheService<TEntity> EntityCacheService { get => ApplicationContext.GetService<EntityCacheService<TEntity>>(); }

        #region 新增,更新,删除 同步方法

        /// <summary>
        /// 把实体entity添加到数据库
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual object Insert(TEntity entity)
        {
            object obj = DbContext.Insert(entity);
            this.OnInserted(entity);
            return obj;
        }


        /// <summary>
        /// 数据库新增实体后自动调用该方法
        /// </summary>
        /// <param name="entity"></param>
        protected virtual void OnInserted(TEntity entity)
        {
            var entityCache = EntityCacheService.GetEntityCache();

            if (entityCache.EnableCache)
            {
                EntityCacheService.IncreaseListCacheVersion(entityCache, entity);
                if (entityCache.PropertyNameOfBody != null)
                {
                    object? val = entityCache.PropertyNameOfBody.GetValue(entity);
                    if (val == null)
                        throw new ArgumentNullException(nameof(val));

                    CacheService.Add(EntityCacheService.GetCacheKeyOfEntityBody(entity.EntityId), val.ToString(), entityCache.CachingExpirationType);
                    entityCache.PropertyNameOfBody.SetValue(entity, null, null);
                }
                CacheService.Add(EntityCacheService.GetCacheKeyOfEntity(entity.EntityId), entity, entityCache.CachingExpirationType);
            }

            if (entity is not SysTableLog && ApplicationContext.CurrentUser != null)
                ApplicationContext.GetService<LocalEventBus>()?.PublishTo(this, new EntityEventArgs(ApplicationContext, DbContext, entity, EntityOperationType.Create));
        }

        /// <summary>
        /// 把实体entiy更新到数据库 
        /// </summary>
        /// <param name="entity"></param>
        public virtual void Update(TEntity entity)
        {
            int num = DbContext.Update(entity);
            if (num > 0)
            {
                this.OnUpdated(entity);
            }
        }

        /// <summary>
        /// 数据库更新实体后自动调用该方法 
        /// </summary>
        /// <param name="entity"></param>
        protected virtual void OnUpdated(TEntity entity)
        {
            var entityCache = EntityCacheService.GetEntityCache();

            if (entityCache.EnableCache)
            {
                EntityCacheService.IncreaseEntityCacheVersion(entityCache, entity.EntityId);
                EntityCacheService.IncreaseListCacheVersion(entityCache, entity);
                if ((entityCache.PropertyNameOfBody != null) && (entityCache.PropertyNameOfBody.GetValue(entity) != null))
                {
                    object? val = entityCache.PropertyNameOfBody.GetValue(entity);
                    if (val == null)
                        throw new ArgumentNullException(nameof(val));

                    CacheService.Set(EntityCacheService.GetCacheKeyOfEntityBody(entity.EntityId), val.ToString(), entityCache.CachingExpirationType);
                    entityCache.PropertyNameOfBody.SetValue(entity, null, null);
                }
                CacheService.Set(EntityCacheService.GetCacheKeyOfEntity(entity.EntityId), entity, entityCache.CachingExpirationType);
            }
            if (entity is not SysTableLog && ApplicationContext.CurrentUser != null)
                ApplicationContext.GetService<LocalEventBus>()?.PublishTo(this, new EntityEventArgs(ApplicationContext, DbContext, entity, EntityOperationType.Update));
        }

        /// <summary>
        /// 从数据库删除实体
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual int Delete(TEntity entity)
        {
            if (entity == null)
            {
                return 0;
            }
            int num = 0;
            if (entity is IDelEntity delEntity)
            {
                delEntity.IsDeletedInDatabase = true;
                num = DbContext.Update(entity);
                if (num > 0)
                {
                    this.OnDeleted(entity);
                }
            }
            else
            {
                num = DbContext.Delete(entity);
                if (num > 0)
                {
                    this.OnDeleted(entity);
                }
            }
            return num;
        }


        /// <summary>
        /// 数据库删除实体后自动调用该方法
        /// </summary>
        /// <param name="entity"></param>
        protected virtual void OnDeleted(TEntity entity)
        {
            var entityCache = EntityCacheService.GetEntityCache();
            if (entityCache.EnableCache)
            {
                EntityCacheService.IncreaseEntityCacheVersion(entityCache, entity.EntityId);
                EntityCacheService.IncreaseListCacheVersion(entityCache, entity);
                CacheService.MarkDeletion(EntityCacheService.GetCacheKeyOfEntity(entity.EntityId), entity, CachingExpirationType.SingleObject);
            }
            if (entity is not SysTableLog && ApplicationContext.CurrentUser != null)
                ApplicationContext.GetService<LocalEventBus>()?.PublishTo(this, new EntityEventArgs(ApplicationContext, DbContext, entity, EntityOperationType.Delete));
        }
        /// <summary>
        /// 从数据库删除实体(by EntityId)
        /// </summary>
        /// <param name="entityId"></param>
        /// <returns></returns>
        public virtual int DeleteByEntityId(object entityId)
        {
            TEntity? entity = Get(entityId);
            if (entity == null)
            {
                return 0;
            }
            return Delete(entity);
        }

        #endregion

        #region 查询-同步方法

        /// <summary>
        /// 依据主键检查实体是否存在于数据库 
        /// </summary>
        /// <param name="entityId"></param>
        /// <returns></returns>
        public bool Exists(object entityId)
        {
            return DbContext.Exists<TEntity>(entityId);
        }

        /// <summary>
        /// 依据EntityId获取单个实体
        /// </summary>
        /// <param name="entityId"></param>
        /// <returns></returns>
        public virtual TEntity? Get(object entityId)
        {
            var entityCache = EntityCacheService.GetEntityCache();
            TEntity? local = default(TEntity);
            if (entityCache.EnableCache)
            {
                local = CacheService.Get<TEntity>(EntityCacheService.GetCacheKeyOfEntity(entityId));
            }
            if (local == null)
            {
                var dao = DbContext;
                local = dao.SingleOrDefault<TEntity>(entityId);
                if (entityCache.EnableCache && (local != null))
                {
                    if (entityCache.PropertyNameOfBody != null)
                    {
                        entityCache.PropertyNameOfBody.SetValue(local, null, null);
                    }
                    CacheService.Add(EntityCacheService.GetCacheKeyOfEntity(local.EntityId), local, entityCache.CachingExpirationType);
                }
            }
            if (local != null)
            {
                if (local is IDelEntity delEntity)
                {
                    if (!delEntity.IsDeletedInDatabase)
                        return local.Clone();
                }
                else
                {
                    return local.Clone();
                }
            }
            return default(TEntity);
        }

        /// <summary>
        /// 获取所有实体（仅用于数据量少的情况）
        /// </summary>
        /// <returns></returns>
        public IEnumerable<TEntity> GetAll()
        {
            return GetAll(string.Empty);
        }

        /// <summary>
        /// 获取所有实体（仅用于数据量少的情况） 
        /// </summary>
        /// <param name="orderBy">排序字段（多个字段用逗号分隔）</param>
        /// <returns>返回按照orderBy排序的所有实体集合</returns>
        public IEnumerable<TEntity> GetAll(string orderBy)
        {
            var entityCache = EntityCacheService.GetEntityCache();
            IEnumerable<object>? enumerable = null;
            string cacheKey = string.Empty;
            if (entityCache.EnableCache)
            {
                cacheKey = EntityCacheService.GetListCacheKeyPrefix(CacheVersionType.GlobalVersion);
                if (!string.IsNullOrEmpty(orderBy))
                {
                    cacheKey = cacheKey + "SB-" + orderBy;
                }
                enumerable = CacheService.Get<IEnumerable<object>>(cacheKey);
            }
            if (enumerable == null)
            {
                PocoData data = PocoData.ForType(typeof(TEntity), new ConventionMapper());
                Sql sql = Sql.Builder.Select(new object[] { data.TableInfo.PrimaryKey }).From(new object[] { data.TableInfo.TableName });
                if (!string.IsNullOrEmpty(orderBy))
                {
                    sql.OrderBy(new object[] { orderBy });
                }
                enumerable = DbContext.FetchFirstColumn<object>(sql);
                if (entityCache.EnableCache)
                {
                    CacheService.Add(cacheKey, enumerable, entityCache.CachingExpirationType);
                }
            }
            return PopulateEntitiesByEntityIds(enumerable);
        }

        /// <summary>
        /// 获取前topNumber条Entity（启用缓存） 
        /// </summary>
        /// <param name="topNumber"></param>
        /// <param name="cachingExpirationTypes">缓存策略</param>
        /// <param name="getCacheKey">生成cacheKey的委托</param>
        /// <param name="generateSql">生成PetaPoco.Sql的委托</param>
        /// <returns></returns>
        protected virtual IEnumerable<TEntity> GetTopEntities(int topNumber, CachingExpirationType cachingExpirationTypes, Func<string> getCacheKey, Func<Sql> generateSql)
        {

            PagingEntityIdCollection? ids = null;
            string cacheKey = getCacheKey();
            ids = CacheService.Get<PagingEntityIdCollection>(cacheKey);
            if (ids == null)
            {
                ids = new PagingEntityIdCollection(DbContext.FetchTopPrimaryKeys<TEntity>(this.SecondaryMaxRecords, generateSql()));
                CacheService.Add(cacheKey, ids, cachingExpirationTypes);
            }
            IEnumerable<object> topEntityIds = ids.GetTopEntityIds(topNumber);
            return PopulateEntitiesByEntityIds(topEntityIds);
        }

        /// <summary>
        /// 依据EntityId集合组装成实体集合（自动缓存） 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entityIds"></param>
        /// <returns></returns>
        public virtual IEnumerable<TEntity> PopulateEntitiesByEntityIds<T>(IEnumerable<T> entityIds)
        {
            entityIds = entityIds.Where(x => x != null).Distinct();
            if (entityIds.Count() == 0)
                return Enumerable.Empty<TEntity>();

            var entityCache = EntityCacheService.GetEntityCache();
            TEntity?[] localArray = new TEntity[entityIds.Count()];
            Dictionary<string, int> dictionary = new Dictionary<string, int>();
            for (int i = 0; i < entityIds.Count(); i++)
            {
                object? objEntityId = entityIds.ElementAt(i);
                if (objEntityId == null)
                    throw new ArgumentNullException(nameof(objEntityId));

                string? entityId = objEntityId.ToString();

                if (string.IsNullOrEmpty(entityId))
                    throw new ArgumentNullException(nameof(entityId));

                string cacheKey = EntityCacheService.GetCacheKeyOfEntity(entityId);
                TEntity? local = CacheService.Get<TEntity>(cacheKey);
                if (local != null)
                {
                    localArray[i] = local;
                }
                else
                {
                    localArray[i] = default(TEntity);

                    dictionary[entityId] = i;
                }
            }
            if (dictionary.Count > 0)
            {
                var dao = this.DbContext;
                dao.OpenSharedConnection();
                IEnumerable<TEntity> entities;
                try
                {
                    entities = dao.FetchByPrimaryKeys<TEntity>(dictionary.Keys);
                }
                finally
                {
                    dao.CloseSharedConnection();
                }
                foreach (TEntity local in entities)
                {
                    if (local.EntityId == null)
                        throw new ArgumentNullException(nameof(local));

                    string? entityId = local.EntityId.ToString();

                    if (string.IsNullOrEmpty(entityId))
                        throw new ArgumentNullException(nameof(local));

                    var index = dictionary[entityId];
                    localArray[index] = local;
                    if (entityCache.EnableCache && (local != null))
                    {
                        if (entityCache.PropertyNameOfBody != null)
                        {
                            entityCache.PropertyNameOfBody.SetValue(local, null, null);
                        }
                        CacheService.Set(EntityCacheService.GetCacheKeyOfEntity(local.EntityId), local, entityCache.CachingExpirationType);
                    }
                }
            }
            List<TEntity> list = new List<TEntity>();
            foreach (TEntity? local in localArray)
            {
                if (local != null)
                {
                    if (local is IDelEntity delEntity && delEntity.IsDeletedInDatabase)
                        continue;

                    list.Add(local);
                }
            }
            return list;
        }

        /// <summary>
        /// (默认)获取分页查询数据（直接读数据库无缓存）
        /// </summary>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <param name="sql"></param>
        /// <returns></returns>
        protected virtual IPagedList<TEntity> GetDefPagingEntities(int pageSize, int pageIndex, Sql sql)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            Page<TEntity> page = DbContext.Page<TEntity>(pageIndex, pageSize, sql);
            stopwatch.Stop();
            TimeSpan timespan = stopwatch.Elapsed;
            return new PagedList<TEntity>(page.Items, pageIndex, pageSize, (int)page.TotalItems, timespan.TotalMilliseconds);
        }

        /// <summary>
        /// 获取分页查询数据 （主键不缓存，实体缓存）
        /// </summary>
        /// <param name="pageSize">每页记录数</param>
        /// <param name="pageIndex">当前页码(从1开始)</param>
        /// <param name="sql"></param>
        /// <returns></returns>
        protected virtual IPagedList<TEntity> GetPagingEntities(int pageSize, int pageIndex, Sql sql)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            PagingEntityIdCollection ids = DbContext.FetchPagingPrimaryKeys<TEntity>(this.PrimaryMaxRecords, pageSize, pageIndex, sql);
            stopwatch.Stop();
            TimeSpan timespan = stopwatch.Elapsed;
            return new PagedList<TEntity>(PopulateEntitiesByEntityIds(ids.GetPagingEntityIds(pageSize, pageIndex)), pageIndex, pageSize, ids.TotalRecords, timespan.TotalMilliseconds);
        }

        /// <summary>
        /// 获取分页查询数据（主键和实体都启用缓存）
        /// </summary>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <param name="cachingExpirationTypes"></param>
        /// <param name="getCacheKey"></param>
        /// <param name="generateSql"></param>
        /// <returns></returns>
        protected virtual IPagedList<TEntity> GetPagingEntities(int pageSize, int pageIndex, CachingExpirationType cachingExpirationTypes, Func<string> getCacheKey, Func<Sql> generateSql)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            PagingEntityIdCollection? ids = null;
            if ((pageIndex < this.CacheablePageCount) && (pageSize <= this.SecondaryMaxRecords))
            {
                string cacheKey = getCacheKey.Invoke();
                ids = CacheService.Get<PagingEntityIdCollection>(cacheKey);
                if (ids == null)
                {
                    ids = DbContext.FetchPagingPrimaryKeys<TEntity>(this.PrimaryMaxRecords, pageSize * this.CacheablePageCount, 1, generateSql());
                    ids.IsContainsMultiplePages = true;
                    CacheService.Add(cacheKey, ids, cachingExpirationTypes);
                }
            }
            else
            {
                ids = DbContext.FetchPagingPrimaryKeys<TEntity>(this.PrimaryMaxRecords, pageSize, pageIndex, generateSql());
            }
            stopwatch.Stop();
            TimeSpan timespan = stopwatch.Elapsed;
            return new PagedList<TEntity>(PopulateEntitiesByEntityIds(ids.GetPagingEntityIds(pageSize, pageIndex)), pageIndex, pageSize, ids.TotalRecords, timespan.TotalMilliseconds);
        }
        #endregion

        #region 新增,更新,删除 异步方法
#if ASYNC
        /// <summary>
        /// 把实体entity添加到数据库
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual async Task<object> InsertAsync(TEntity entity)
        {
            object obj = await DbContext.InsertAsync(entity);
            await this.OnInsertedAsync(entity);
            return obj;
        }


        /// <summary>
        /// 数据库新增实体后自动调用该方法
        /// </summary>
        /// <param name="entity"></param>
        protected virtual async Task OnInsertedAsync(TEntity entity)
        {
            var entityCache = await EntityCacheService.GetEntityCacheAsync();

            if (entityCache.EnableCache)
            {
                EntityCacheService.IncreaseListCacheVersion(entityCache, entity);
                if (entityCache.PropertyNameOfBody != null)
                {
                    object? val = entityCache.PropertyNameOfBody.GetValue(entity);
                    if (val == null)
                        throw new ArgumentNullException(nameof(val));

                    await CacheService.AddAsync(await EntityCacheService.GetCacheKeyOfEntityBodyAsync(entity.EntityId), val.ToString(), entityCache.CachingExpirationType);
                    entityCache.PropertyNameOfBody.SetValue(entity, null, null);
                }
                await CacheService.AddAsync(await EntityCacheService.GetCacheKeyOfEntityAsync(entity.EntityId), entity, entityCache.CachingExpirationType);
            }
        }

        /// <summary>
        /// 把实体entiy更新到数据库 
        /// </summary>
        /// <param name="entity"></param>
        public virtual async Task UpdateAsync(TEntity entity)
        {
            int num = await DbContext.UpdateAsync(entity);
            if (num > 0)
            {
                await this.OnUpdatedAsync(entity);
            }
        }

        /// <summary>
        /// 数据库更新实体后自动调用该方法 
        /// </summary>
        /// <param name="entity"></param>
        protected virtual async Task OnUpdatedAsync(TEntity entity)
        {
            var entityCache = await EntityCacheService.GetEntityCacheAsync();

            if (entityCache.EnableCache)
            {
                EntityCacheService.IncreaseEntityCacheVersion(entityCache, entity.EntityId);
                EntityCacheService.IncreaseListCacheVersion(entityCache, entity);
                if ((entityCache.PropertyNameOfBody != null) && (entityCache.PropertyNameOfBody.GetValue(entity) != null))
                {
                    object? val = entityCache.PropertyNameOfBody.GetValue(entity);
                    if (val == null)
                        throw new ArgumentNullException(nameof(val));

                    await CacheService.SetAsync(await EntityCacheService.GetCacheKeyOfEntityBodyAsync(entity.EntityId), val.ToString(), entityCache.CachingExpirationType);
                    entityCache.PropertyNameOfBody.SetValue(entity, null, null);
                }
                await CacheService.SetAsync(await EntityCacheService.GetCacheKeyOfEntityAsync(entity.EntityId), entity, entityCache.CachingExpirationType);
            }
        }

        /// <summary>
        /// 从数据库删除实体
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual async Task<int> DeleteAsync(TEntity entity)
        {
            if (entity == null)
            {
                return 0;
            }
            int num = 0;
            if (entity is IDelEntity delEntity)
            {
                delEntity.IsDeletedInDatabase = true;
                num = await DbContext.UpdateAsync(entity);
                if (num > 0)
                {
                    await this.OnDeletedAsync(entity);
                }
            }
            else
            {
                num = await DbContext.DeleteAsync(entity);
                if (num > 0)
                {
                    await this.OnDeletedAsync(entity);
                }
            }
            return num;
        }


        /// <summary>
        /// 数据库删除实体后自动调用该方法
        /// </summary>
        /// <param name="entity"></param>
        protected virtual async Task OnDeletedAsync(TEntity entity)
        {
            var entityCache = await EntityCacheService.GetEntityCacheAsync();
            if (entityCache.EnableCache)
            {
                EntityCacheService.IncreaseEntityCacheVersion(entityCache, entity.EntityId);
                EntityCacheService.IncreaseListCacheVersion(entityCache, entity);
                await CacheService.MarkDeletionAsync(await EntityCacheService.GetCacheKeyOfEntityAsync(entity.EntityId), entity, CachingExpirationType.SingleObject);
            }
        }
        /// <summary>
        /// 从数据库删除实体(by EntityId)
        /// </summary>
        /// <param name="entityId"></param>
        /// <returns></returns>
        public virtual async Task<int> DeleteByEntityIdAsync(object entityId)
        {
            TEntity? entity = await GetAsync(entityId);
            if (entity == null)
            {
                return 0;
            }
            return await DeleteAsync(entity);
        }
#endif
        #endregion

        #region 查询-异步方法
#if ASYNC
        /// <summary>
        /// 依据主键检查实体是否存在于数据库 
        /// </summary>
        /// <param name="entityId"></param>
        /// <returns></returns>
        public async Task<bool> ExistsAsync(object entityId)
        {
            return await DbContext.ExistsAsync<TEntity>(entityId);
        }
   
        /// <summary>
        /// 依据EntityId获取单个实体
        /// </summary>
        /// <param name="entityId"></param>
        /// <returns></returns>
        public virtual async Task<TEntity?> GetAsync(object entityId)
        {
            var entityCache = await EntityCacheService.GetEntityCacheAsync();
            TEntity? local = default(TEntity);
            if (entityCache.EnableCache)
            {
                local = await CacheService.GetAsync<TEntity>(await EntityCacheService.GetCacheKeyOfEntityAsync(entityId));
            }
            if (local == null)
            {
                var dao = DbContext;
                local = await dao.SingleOrDefaultAsync<TEntity>(entityId);
                if (entityCache.EnableCache && (local != null))
                {
                    if (entityCache.PropertyNameOfBody != null)
                    {
                        entityCache.PropertyNameOfBody.SetValue(local, null, null);
                    }
                    await CacheService.AddAsync(await EntityCacheService.GetCacheKeyOfEntityAsync(local.EntityId), local, entityCache.CachingExpirationType);
                }
            }
            if (local != null)
            {
                if (local is IDelEntity delEntity)
                {
                    if (!delEntity.IsDeletedInDatabase)
                        return local;
                }
                else
                {
                    return local;
                }
            }
            return default(TEntity);
        }

        /// <summary>
        /// 获取所有实体（仅用于数据量少的情况）
        /// </summary>
        /// <returns></returns>
        public async Task<IEnumerable<TEntity>> GetAllAsync()
        {
            return await GetAllAsync(string.Empty);
        }

        /// <summary>
        /// 获取所有实体（仅用于数据量少的情况） 
        /// </summary>
        /// <param name="orderBy">排序字段（多个字段用逗号分隔）</param>
        /// <returns>返回按照orderBy排序的所有实体集合</returns>
        public async Task<IEnumerable<TEntity>> GetAllAsync(string orderBy)
        {
            var entityCache = await EntityCacheService.GetEntityCacheAsync();
            IEnumerable<object>? enumerable = null;
            string cacheKey = string.Empty;
            if (entityCache.EnableCache)
            {
                cacheKey = await EntityCacheService.GetListCacheKeyPrefixAsync(CacheVersionType.GlobalVersion);
                if (!string.IsNullOrEmpty(orderBy))
                {
                    cacheKey = cacheKey + "SB-" + orderBy;
                }
                enumerable = await CacheService.GetAsync<IEnumerable<object>>(cacheKey);
            }
            if (enumerable == null)
            {
                PocoData data = PocoData.ForType(typeof(TEntity), new ConventionMapper());
                Sql sql = Sql.Builder.Select(new object[] { data.TableInfo.PrimaryKey }).From(new object[] { data.TableInfo.TableName });
                if (!string.IsNullOrEmpty(orderBy))
                {
                    sql.OrderBy(new object[] { orderBy });
                }
                enumerable = await DbContext.FetchFirstColumnAsync<object>(sql);
                if (entityCache.EnableCache)
                {
                    await CacheService.AddAsync(cacheKey, enumerable, entityCache.CachingExpirationType);
                }
            }
            return await PopulateEntitiesByEntityIdsAsync(enumerable);
        }

        /// <summary>
        /// 获取前topNumber条Entity（启用缓存） 
        /// </summary>
        /// <param name="topNumber"></param>
        /// <param name="cachingExpirationTypes">缓存策略</param>
        /// <param name="getCacheKey">生成cacheKey的委托</param>
        /// <param name="generateSql">生成PetaPoco.Sql的委托</param>
        /// <returns></returns>
        protected virtual async Task<IEnumerable<TEntity>> GetTopEntitiesAsync(int topNumber, CachingExpirationType cachingExpirationTypes, Func<string> getCacheKey, Func<Sql> generateSql)
        {

            PagingEntityIdCollection? ids = null;
            string cacheKey = getCacheKey();
            ids = await CacheService.GetAsync<PagingEntityIdCollection>(cacheKey);
            if (ids == null)
            {
                ids = new PagingEntityIdCollection(await DbContext.FetchTopPrimaryKeysAsync<TEntity>(this.SecondaryMaxRecords, generateSql()));
                await CacheService.AddAsync(cacheKey, ids, cachingExpirationTypes);
            }
            IEnumerable<object> topEntityIds = ids.GetTopEntityIds(topNumber);
            return await PopulateEntitiesByEntityIdsAsync(topEntityIds);
        }

        /// <summary>
        /// 依据EntityId集合组装成实体集合（自动缓存） 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entityIds"></param>
        /// <returns></returns>
        public virtual async Task<IEnumerable<TEntity>> PopulateEntitiesByEntityIdsAsync<T>(IEnumerable<T> entityIds)
        {
            var entityCache = await EntityCacheService.GetEntityCacheAsync();
            TEntity?[] localArray = new TEntity[entityIds.Count()];
            Dictionary<string, int> dictionary = new Dictionary<string, int>();
            for (int i = 0; i < entityIds.Count(); i++)
            {
                object? objEntityId = entityIds.ElementAt(i);
                if (objEntityId == null)
                    throw new ArgumentNullException(nameof(objEntityId));

                string? entityId = objEntityId.ToString();

                if (string.IsNullOrEmpty(entityId))
                    throw new ArgumentNullException(nameof(entityId));

                string cacheKey = await EntityCacheService.GetCacheKeyOfEntityAsync(entityId);
                TEntity local = await CacheService.GetAsync<TEntity>(cacheKey);
                if (local != null)
                {
                    localArray[i] = local;
                }
                else
                {
                    localArray[i] = default(TEntity);

                    dictionary[entityId] = i;
                }
            }
            if (dictionary.Count > 0)
            {
                var dao = this.DbContext;
                await dao.OpenSharedConnectionAsync();
                IEnumerable<TEntity> entities;
                try
                {
                    entities = await dao.FetchByPrimaryKeysAsync<TEntity>(dictionary.Keys);
                }
                finally
                {
                    dao.CloseSharedConnection();
                }
                foreach (TEntity local in entities)
                {
                    if (local.EntityId == null)
                        throw new ArgumentNullException(nameof(local));

                    string? entityId = local.EntityId.ToString();

                    if (string.IsNullOrEmpty(entityId))
                        throw new ArgumentNullException(nameof(local));

                    var index = dictionary[entityId];
                    localArray[index] = local;
                    if (entityCache.EnableCache && (local != null))
                    {
                        if (entityCache.PropertyNameOfBody != null)
                        {
                            entityCache.PropertyNameOfBody.SetValue(local, null, null);
                        }
                        await CacheService.SetAsync(await EntityCacheService.GetCacheKeyOfEntityAsync(local.EntityId), local, entityCache.CachingExpirationType);
                    }
                }
            }
            List<TEntity> list = new List<TEntity>();
            foreach (TEntity? local in localArray)
            {
                if (local != null)
                {
                    if (local is IDelEntity delEntity && delEntity.IsDeletedInDatabase)
                        continue;

                    list.Add(local);
                }
            }
            return list;
        }

        /// <summary>
        /// (默认)获取分页查询数据（直接读数据库无缓存）
        /// </summary>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <param name="sql"></param>
        /// <returns></returns>
        protected virtual async Task<IPagedList<TEntity>> GetDefPagingEntitiesAsync(int pageSize, int pageIndex, Sql sql)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            Page<TEntity> page = await DbContext.PageAsync<TEntity>(pageIndex, pageSize, sql);
            stopwatch.Stop();
            TimeSpan timespan = stopwatch.Elapsed;
            return new PagedList<TEntity>(page.Items, pageIndex, pageSize, page.TotalItems) { Duration = timespan.TotalSeconds };
        }

        /// <summary>
        /// 获取分页查询数据 （主键不缓存，实体缓存）
        /// </summary>
        /// <param name="pageSize">每页记录数</param>
        /// <param name="pageIndex">当前页码(从1开始)</param>
        /// <param name="sql"></param>
        /// <returns></returns>
        protected virtual async Task<IPagedList<TEntity>> GetPagingEntitiesAsync(int pageSize, int pageIndex, Sql sql)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            PagingEntityIdCollection ids = await DbContext.FetchPagingPrimaryKeysAsync<TEntity>(this.PrimaryMaxRecords, pageSize, pageIndex, sql);
            stopwatch.Stop();
            TimeSpan timespan = stopwatch.Elapsed;
            return new PagedList<TEntity>(await PopulateEntitiesByEntityIdsAsync(ids.GetPagingEntityIds(pageSize, pageIndex)), pageIndex, pageSize, ids.TotalRecords) { Duration = timespan.TotalSeconds };
        }

        /// <summary>
        /// 获取分页查询数据（主键和实体都启用缓存）
        /// </summary>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <param name="cachingExpirationTypes"></param>
        /// <param name="getCacheKey"></param>
        /// <param name="generateSql"></param>
        /// <returns></returns>
        protected virtual async Task<IPagedList<TEntity>> GetPagingEntitiesAsync(int pageSize, int pageIndex, CachingExpirationType cachingExpirationTypes, Func<string> getCacheKey, Func<Sql> generateSql)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            PagingEntityIdCollection? ids = null;
            if ((pageIndex < this.CacheablePageCount) && (pageSize <= this.SecondaryMaxRecords))
            {
                string cacheKey = getCacheKey.Invoke();
                ids = await CacheService.GetAsync<PagingEntityIdCollection>(cacheKey);
                if (ids == null)
                {
                    ids = await DbContext.FetchPagingPrimaryKeysAsync<TEntity>(this.PrimaryMaxRecords, pageSize * this.CacheablePageCount, 1, generateSql());
                    ids.IsContainsMultiplePages = true;
                    await CacheService.AddAsync(cacheKey, ids, cachingExpirationTypes);
                }
            }
            else
            {
                ids = await DbContext.FetchPagingPrimaryKeysAsync<TEntity>(this.PrimaryMaxRecords, pageSize, pageIndex, generateSql());
            }
            stopwatch.Stop();
            TimeSpan timespan = stopwatch.Elapsed;
            return new PagedList<TEntity>(await PopulateEntitiesByEntityIdsAsync(ids.GetPagingEntityIds(pageSize, pageIndex)), pageIndex, pageSize, ids.TotalRecords) { Duration = timespan.TotalSeconds };
        }
#endif
        #endregion

    }
}
